<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Race Chart</title>
  <!-- Bootstrap Styles-->
  <link href="assets/css/bootstrap.css" rel="stylesheet" />
  <!-- FontAwesome Styles-->
  <link href="assets/css/font-awesome.css" rel="stylesheet" />
  <!-- Morris Chart Styles-->
  <link href="assets/js/morris/morris-0.4.3.min.css" rel="stylesheet" />
  <!-- Custom Styles-->
  <link href="assets/css/custom-styles.css" rel="stylesheet" />
  <!-- Google Fonts-->
  <link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css' />
</head>

<body>
  <script src="./d3.min.js"></script>
  <div id="wrapper">
    <nav class="navbar navbar-default top-navbar" role="navigation">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".sidebar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="index.html">高管宝典</a>
        </div>  
    </nav>
    <!--/. NAV TOP  -->
    <nav class="navbar-default navbar-side" role="navigation">
        <div class="sidebar-collapse">
            <ul class="nav" id="main-menu">
                <li>
                    <a href="index.html"><i class="fa fa-dashboard"></i>概览</a>
                </li>
                <li>
                    <a class="active-menu" href="race.html?feature=institute"><i class="fa fa-desktop"></i> 时间追逐柱状图</a>
                </li>
                <li>
                    <a href="statistics.html?feature=age,salary"><i class="fa fa-bar-chart-o"></i> 多因素统计</a>
                </li>
                <li>
                  <a href="personal.html?name=张锡康"><i class="fa fa-search"></i> 个人搜索</a>
                </li>
                <li>
                  <a href="map.html"><i class="fa fa-arrows"></i> 关系映射图 </a>
              </li>
            </ul>

        </div>

    </nav>
    <!-- /. NAV SIDE  -->
    <div id="page-wrapper">
        <div id="page-inner">

          <div class="row">
            <div class="col-md-12">
                <h1 class="page-header">
                    时间追逐柱状图 <small>学历、薪资、任教大学、专业分布随时间变化关系</small>
                </h1>
            </div>
        </div>

        <div class="row">

          <div class="btn-group col-md-10">
            <button style="font-size: 20px" data-toggle="dropdown" class="btn btn-info dropdown-toggle"> 选择变量 <span class="caret"></span></button>
            <ul class="dropdown-menu">
            <li><a href="race.html?feature=degree">学历</a></li>
            <li><a href="race.html?feature=salary">薪资</a></li>
            <li><a href="race.html?feature=institute">任教大学 </a></li>
            <li><a href="race.html?feature=department">专业 </a></li>
            </ul>
          </div>

          <div class="col-md-2 d-flex justify-content-end">
            <button type="button" class="btn btn-default btn-circle" id="startplay"><i class="fa fa-play"></i>
            </button>
            <button type="button" class="btn btn-default btn-circle" id="stop"><i class="fa fa-pause"></i>
            </button>
          </div>
      </div>
      
        <svg width="1100" height="600" id="mainsvg" class="svgs"></svg>
  <script>
      // The following code is the typical routine of my d3.js code.

              //Read the data
      feature = window.location.search.split("=")[1];
      xhr = new XMLHttpRequest();
      xhr.open('POST', 'http://127.0.0.1:5000/login', true);
      xhr.setRequestHeader('Content-Type', 'application/json');  // 设置POST传参的请求头(JSON)
      xhr.onreadystatechange = function () {
        if (xhr.readyState !== 4) return;  // readyState == 4 -> 已接收到全部响应数据
        if (xhr.status < 200 || xhr.status >= 300) return;  // 200 <= status <= 299 -> 请求成功
        data = JSON.parse(xhr.responseText)
        console.log(data)
        // Labels of row and columns -> unique identifier of the column called 'group' and 'variable'


        const svg = d3.select('svg');
        const width = svg.attr('width');
        duration = 150
        n = 6
        k = 10
        barSize = 81
        margin = ({top: 30, right: 6, bottom: 6, left: 10})
        height = margin.top + barSize * n + margin.bottom

        /*
          Loading data and preprocessing data.
          Note that you can also preprocessing data in your own way using your prefered language, e.g., Python.
        */
        async function runAnimation() {
          console.log(data)
          names = new Set(data.map(d => d.name))

          datevalues = Array.from(d3.rollup(data, d => d[0].value, d => new Date(d.date), d => d.name))
          .map(([date, data]) => [date, data])
          .sort(([a], [b]) => d3.ascending(a, b))

          console.log(datevalues)

          function rank(value) {
            const data = Array.from(names, name => ({name, value: value(name)}));
            data.sort((a, b) => d3.descending(a.value, b.value));
            for (let i = 0; i < data.length; ++i) data[i].rank = Math.min(n, i);
            return data;
          }
          //keyframes
          const keyframes = [];
          let ka, a, kb, b;
          console.log(datevalues)
          for ([[ka, a], [kb, b]] of d3.pairs(datevalues)) {
            for (let i = 0; i < k; ++i) {
              const t = i / k;
              keyframes.push([
                new Date(ka * (1 - t) + kb * t),
                rank(name => (a.get(name) || 0) * (1 - t) + (b.get(name) || 0) * t)
              ]);
            }
          }
          console.log(b)
          keyframes.push([new Date(kb), rank(name => b.get(name) || 0)]);

          //My code
          let dateObjects = keyframes.map(date => new Date(date[0]));
          // let invertDate2index = keyframes.map((date, i) => [new Date(date[0]), i]);
          // make invertDate2index a map
          // invertDate2index = new Map(invertDate2index);
          
          console.log(dateObjects)

          time_axis_height = height + 50
          time_axis_width = width - margin.right - 40
          let datex = d3.scaleTime()
              .domain(d3.extent(dateObjects)) // 这会获取日期的最小值和最大值
              .range([0, time_axis_width]);
          xXis = svg.append("g")
              .attr("class", "x axis")
              .attr("transform", "translate(20," + time_axis_height + ")")
          xXis.append('g').call(d3.axisBottom(datex))
              .selectAll("text")
              .style("font-size", "20px")
          let circle = xXis.append("circle")
              .attr("r", 10)
              .attr("cy", 0)
              .attr("cx", datex(dateObjects[0]));


          let drag = d3.drag()
              .on("start", dragstarted)
              .on("drag", dragged)
              .on("end", dragended);

          // 将拖拽行为绑定到圆形元素上
          circle.call(drag);


          nameframes = d3.groups(keyframes.flatMap(([, data]) => data), d => d.name)
          prev = new Map(nameframes.flatMap(([, data]) => d3.pairs(data, (a, b) => [b, a])))
          next = new Map(nameframes.flatMap(([, data]) => d3.pairs(data)))
          function bars(svg) {
            let bar = svg.append("g")
                .attr("fill-opacity", 0.6)
              .selectAll("rect");

            return ([date, data], transition) => bar = bar
              .data(data.slice(0, n), d => d.name)
              .join(
                enter => enter.append("rect")
                  .attr("fill", color)
                  .attr("height", y.bandwidth())
                  .attr("x", x(0))
                  .attr("y", d => y((prev.get(d) || d).rank))
                  .attr("width", d => x((prev.get(d) || d).value) - x(0)),
                update => update,
                exit => exit.transition(transition).remove()
                  .attr("y", d => y((next.get(d) || d).rank))
                  .attr("width", d => x((next.get(d) || d).value) - x(0))
              )
              .call(bar => bar.transition(transition)
                .attr("y", d => y(d.rank))
                .attr("width", d => x(d.value) - x(0)));
          }
          function labels(svg) {
            let label = svg.append("g")
                //.style("font", "bold 12px var(--sans-serif)")
                .style("font-size", "16px")
                .style("font-weight", "bold")
                .style("font-variant-numeric", "tabular-nums")
                .attr("text-anchor", "end")
              .selectAll("text");

            return ([date, data], transition) => label = label
              .data(data.slice(0, n), d => d.name)
              .join(
                enter => enter.append("text")
                  .attr("transform", d => `translate(${x((prev.get(d) || d).value)},${y((prev.get(d) || d).rank)})`)
                  .attr("y", y.bandwidth() / 2)
                  .attr("x", -6)
                  .attr("dy", "-0.25em")
                  .text(d => d.name)
                  .call(text => text.append("tspan")
                    .attr("fill-opacity", 0.7)
                    .attr("font-weight", "normal")
                    .attr("x", -6)
                    .attr("dy", "1.15em")),
                update => update,
                exit => exit.transition(transition).remove()
                  .attr("transform", d => `translate(${x((next.get(d) || d).value)},${y((next.get(d) || d).rank)})`)
                  .call(g => g.select("tspan").tween("text", d => textTween(d.value, (next.get(d) || d).value)))
              )
              .call(bar => bar.transition(transition)
                .attr("transform", d => `translate(${x(d.value)},${y(d.rank)})`)
                .call(g => g.select("tspan").tween("text", d => textTween((prev.get(d) || d).value, d.value))));
          }
          function textTween(a, b) {
            const i = d3.interpolateNumber(a, b);
            return function(t) {
              this.textContent = formatNumber(i(t));
            };
          }
          formatNumber = d3.format(",d")
          tickFormat = undefined
          function axis(svg) {
            const g = svg.append("g")
                .attr("transform", `translate(0,${margin.top})`);

            const axis = d3.axisTop(x)
                .ticks(width / 160, tickFormat)
                .tickSizeOuter(0)
                .tickSizeInner(-barSize * (n + y.padding()))
            return (_, transition) => {
              g.transition(transition).call(axis).selectAll("text")
                  .style("font-size", "16px");
              g.select(".tick:first-of-type text").remove();
              g.selectAll(".tick:not(:first-of-type) line").attr("stroke", "white");
              g.select(".domain").remove();
            };
          }
          function ticker(svg) {
            const now = svg.append("text")
                //.style("font", `bold ${barSize}px var(--sans-serif)`)
                .style("font-size", "32px")
                .style("font-weight", "bold")
                .style("font-variant-numeric", "tabular-nums")
                .attr("text-anchor", "end")
                .attr("x", width - 6)
                .attr("y", margin.top + barSize * (n - 0.45))
                .attr("dy", "0.32em")
                .text(formatDate(keyframes[0][0]));

            return ([date], transition) => {
              transition.end().then(() => now.text(formatDate(date)));
            };
          }
          formatDate = d3.utcFormat("%Y")
          color = function(d) {
            const scale = d3.scaleOrdinal(d3.schemeTableau10);
            if (data.some(d => d.category !== undefined)) {
              const categoryByName = new Map(data.map(d => [d.name, d.category]))
              scale.domain(categoryByName.values());
              return scale(categoryByName.get(d.name));
            }
            else {
              const ByName = new Set(data.map(d => d.name))
              scale.domain(ByName);
              console.log(ByName)
              console.log(scale(d.name))
              return scale(d.name);
            }
          }
          x = d3.scaleLinear([0, 1], [margin.left, width - margin.right])
          y = d3.scaleBand()
          .domain(d3.range(n + 1))
          .rangeRound([margin.top, margin.top + barSize * (n + 1 + 0.1)])
          .padding(0.2)

          const updateBars = bars(svg);
          const updateAxis = axis(svg);
          const updateLabels = labels(svg);
          const updateTicker = ticker(svg);

          // yield svg.node();
          async function gotoKeyFrame(keyframe){
            const transition = svg.transition()
                .duration(duration)
                .ease(d3.easeLinear);

            // Extract the top bar’s value.
            x.domain([0, keyframe[1][0].value]);

            updateAxis(keyframe, transition);
            updateBars(keyframe, transition);
            updateLabels(keyframe, transition);
            updateTicker(keyframe, transition);
            circle.transition(transition)
              .attr("cx", datex(keyframe[0]))
            console.log(datex(keyframe[0]))
            // invalidation.then(() => svg.interrupt());
            await transition.end();
          }
          gotoKeyFrame(keyframes[0])
          let currentFrame = 0
          console.log(keyframes)

          function dragstarted(event) {
            d3.select(this).raise();
          }

          function dragged(event) {
            // 限制圆形元素在x轴上的移动范围
            let newX = Math.max(0, Math.min(time_axis_width, event.x));
            d3.select(this).attr("cx", newX)
              .attr("cy", 0);
            let currentDate = datex.invert(newX);
            console.log(currentDate); // 输出用户拖拽的日期
          }

          function dragended(event) {
            let newX = Math.max(0, Math.min(time_axis_width, event.x));
            d3.select(this).attr("cx", newX)
              .attr("cy", 0);
            let currentDate = datex.invert(newX);
            //find the nearest date
            let nearestDate = d3.bisectLeft(dateObjects, currentDate);
            gotoKeyFrame(keyframes[nearestDate])
            currentFrame = nearestDate
          }
          let isRunning = false
          d3.selectAll("#startplay").on("click", async function() {
            isRunning = true
              for (i = currentFrame + 1; i < keyframes.length; i++) {
                await gotoKeyFrame(keyframes[i])
                currentFrame = i
                if(!isRunning){
                  break
                }
              }
          });
          d3.selectAll("#stop").on("click", function() {
            isRunning = false
          });
          
        // });
        }

        runAnimation();
      }
     xhr.send(JSON.stringify({type:"race", feature: feature, password: 123456}));  // 发送请求, 这里设置POST请求的附加参数



  </script>
</div>
<!-- /. PAGE INNER  -->
</div>
<!-- /. PAGE WRAPPER  -->
</div>
<!-- /. WRAPPER  -->
<!-- JS Scripts-->
<!-- jQuery Js -->
<script src="assets/js/jquery-1.10.2.js"></script>
<!-- Bootstrap Js -->
<script src="assets/js/bootstrap.min.js"></script>

<!-- Metis Menu Js -->
<script src="assets/js/jquery.metisMenu.js"></script>
<!-- Morris Chart Js -->
<script src="assets/js/morris/raphael-2.1.0.min.js"></script>
<script src="assets/js/morris/morris.js"></script>


<script src="assets/js/easypiechart.js"></script>
<script src="assets/js/easypiechart-data.js"></script>


<!-- Custom Js -->
<script src="assets/js/custom-scripts.js"></script>
</body>

</html>